import numpy as np
from sko.GA import GA_TSP

from Utils.mysql_utils import DBHelper
from production_scheduling.PMPUtils import PMPAssignment
import random

dbhelper = DBHelper(host='127.0.0.1', user='root', pwd='root', db='pmp_train')


# 计算适应度，求整体最小值
def calFitness(order_sequence_np):
    pmpAssignment = PMPAssignment(line_list, product_list, product_cost_matrix, transport_cost_matrix,
                                  order_sequence_np)
    pmpAssignment.productAssign()
    # pmpAssignment.drawGantt()
    return pmpAssignment.getFitness()


# 将工作顺序编码为类别
def sequence2productClass(product_list, best_product_sequence):
    best_product_class_sequence = []
    for val in best_product_sequence:
        best_product_class_sequence.append(product_list[val]['product_class_id'])
    return best_product_class_sequence


# 计算一个环境的结果
def eachEvnProductClass(line_list, product_list, product_cost_matrix, transport_cost_matrix):
    # 遗传算法规划问题
    ga_tsp = GA_TSP(func=calFitness, n_dim=len(product_list), size_pop=500, max_iter=100, prob_mut=0.1)
    best_product_sequence, best_fitness = ga_tsp.run()
    best_product_class_sequence = sequence2productClass(product_list, best_product_sequence)
    return best_product_class_sequence, best_fitness[0]


# 设计一种环境
def createEachEnv(line_list, product_class_cost_matrix, transport_class_cost_matrix, train_input):
    # 展开订单数据
    product_class_extend_list = []
    for index, val in enumerate(train_input):
        temp_product_class_id = val['product_class_id']
        temp_product_class_name = val['product_class_name']
        temp_num = val['num']
        for i in range(temp_num):
            temp_product = {'product_class_id': temp_product_class_id,
                            'product_class_name': temp_product_class_name}
            product_class_extend_list.append(temp_product)
    # 加载订单数据
    product_list = []
    for index, val in enumerate(product_class_extend_list):
        temp_product = {'product_id': index, 'product_name': str(index) + '订单',
                        'product_class_id': val['product_class_id'],
                        'product_class_name': val['product_class_name']}
        product_list.append(temp_product)

    # 加载订单开销
    product_cost_matrix = []
    for line in line_list:
        line_id = line['line_id']
        each_line_product_cost = []
        for product in product_list:
            product_class_id = product['product_class_id']
            product_cost = product_class_cost_matrix[line_id][product_class_id]
            each_line_product_cost.append(product_cost)
        product_cost_matrix.append(each_line_product_cost)

    # 加载订单转产开销数据
    transport_cost_matrix = []
    for org in product_list:
        org_class_id = org['product_class_id']
        each_product_transport_cost_list = []
        for target in product_list:
            target_class_id = target['product_class_id']
            # 每一个任务到达别的任务需要的时间
            each_product_transport_cost = transport_class_cost_matrix[org_class_id][target_class_id]
            each_product_transport_cost_list.append(each_product_transport_cost)
        # 最后一列加一个1，当做起点时候要做的准备，以后可以通过设置读取
        each_product_transport_cost_list.append(1)
        transport_cost_matrix.append(each_product_transport_cost_list)
    # 加一行都是1的，表示起点到每个任务的时间
    last_rows = []
    for i in range(len(product_list) + 1):
        last_rows.append(1)
    transport_cost_matrix.append(last_rows)

    # 返回初始序列
    org_class_squence = []
    for product_i in product_list:
        org_class_squence.append(product_i['product_class_id'])

    return line_list, product_list, product_cost_matrix, transport_cost_matrix, org_class_squence


# 基本静态信息
def getBaseInfo():
    # 产线信息，固定为三个
    line_list = [
        {'line_id': 0, 'line_name': '第一产线'},
        {'line_id': 1, 'line_name': '第二产线'},
        {'line_id': 2, 'line_name': '第三产线'},
    ]
    # 生产任务信息
    product_class = [
        {'product_class_id': 0, 'product_class_name': '车型1'},
        {'product_class_id': 1, 'product_class_name': '车型2'},
        {'product_class_id': 2, 'product_class_name': '车型3'},
    ]
    # 工时信息，用二维数组表示,表示每个产线完成每个类别任务所需要的时间，行数为产线号，列为生产任务号，
    # 比如第一行，第四列表示1号产线（line_id为0）完成3类任务（product_id为2）的时间
    product_class_cost_matrix = [
        [3, 4, 5],
        [3, 5, 5],
        [2, 3, 4],
    ]
    # 固定生成4*4的转产开销矩阵，默认第一行为第一类任务到各类任务的转产时间开销，
    # 多余的最后一行一列表示从开始到生产该机型的时间，目前全部是1
    transport_class_cost_matrix = [
        [1, 1, 1],
        [1, 1, 1],
        [1, 1, 1],
    ]
    # 订单总数，这不能变
    total_num = 12
    return line_list, product_class, product_class_cost_matrix, transport_class_cost_matrix, total_num


# 随机一个训练数据
def randTrainInput(product_class, total_num):
    train_input = []
    temp_rand = total_num
    for index, val in enumerate(product_class):
        if index == len(product_class) - 1:
            rand_num = temp_rand
        else:
            rand_num = random.randint(0, temp_rand)
        temp_train_input = {
            'product_class_id': val['product_class_id'],
            'product_class_name': val['product_class_name'],
            'num': rand_num
        }
        train_input.append(temp_train_input)
        temp_rand = temp_rand - rand_num
    return train_input


# 将数据写入数据库
def writeResult(org_class_sequence, best_class_sequence, best_fitness):
    org_class_sequence_str = "".join(str(org_class_sequence))
    best_class_sequence_str = "".join(str(best_class_sequence))
    sql = "insert into pmp_ga_train(org_class_sequence,best_class_sequence,best_fitness)" \
          "values ( %(org_class_sequence)s,%(best_class_sequence)s,%(best_fitness)s ) "

    dbhelper.execute(sql, {'org_class_sequence': org_class_sequence_str,
                           'best_class_sequence': best_class_sequence_str,
                           'best_fitness': best_fitness})

    pass


# 判断是否已经有值
def ifHasKey(org_class_sequence):
    org_class_sequence_str = "".join(str(org_class_sequence))
    sql = "select * from pmp_ga_train where org_class_sequence=%(org_class_sequence)s"

    rs = dbhelper.fetchall(sql, {'org_class_sequence': org_class_sequence_str})
    ifHas = False
    if len(rs) != 0:
        ifHas = True
    return ifHas


if __name__ == '__main__':
    # 获取基本信息，这里是不变的
    line_list, product_class, product_class_cost_matrix, transport_class_cost_matrix, total_num = getBaseInfo()

    epoch_num = 1000
    for i in range(0, epoch_num):
        # 训练信息
        train_input = randTrainInput(product_class, total_num)

        # 获取一个环境的数据
        line_list, product_list, product_cost_matrix, transport_cost_matrix, org_class_squence = \
            createEachEnv(line_list, product_class_cost_matrix, transport_class_cost_matrix, train_input)

        # 判断已经有了测试数据
        ifHas = ifHasKey(org_class_squence)

        if ifHas != True:
            # 计算一个环境的结果
            best_product_class_sequence, best_fitness = eachEvnProductClass(line_list, product_list,
                                                                            product_cost_matrix,
                                                                            transport_cost_matrix)

            print(org_class_squence, best_product_class_sequence, best_fitness)
            # 写入数据库
            writeResult(org_class_squence, best_product_class_sequence, best_fitness)
